package model

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"time"
	"wxpay/pkg/util"

	"gorm.io/datatypes"
)

// Transfer struct is a row record of the transfer table in the wxpay database
type Transfer struct {
	//[ 0] id                                             INT8                 null: false  primary: true   isArray: false  auto: false  col: INT8            len: -1      default: []
	ID int64 `gorm:"primary_key;column:id;type:INT8;"`
	//[ 1] out_batch_no                                   VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	OutBatchNo string `gorm:"column:out_batch_no;type:VARCHAR;size:32;"`
	//[ 2] name                                           VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	Name string `gorm:"column:name;type:VARCHAR;size:32;"`
	//[ 3] remark                                         VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	Remark string `gorm:"column:remark;type:VARCHAR;size:32;"`
	//[ 4] app_id                                         VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	AppID string `gorm:"column:app_id;type:VARCHAR;size:32;"`
	//[ 5] amount                                         INT8                 null: false  primary: false  isArray: false  auto: false  col: INT8            len: -1      default: []
	Amount int64 `gorm:"column:amount;type:INT8;"`
	//[ 6] count                                          INT8   		       null: false  primary: false  isArray: false  auto: false  col: INT8         	  len: -1      default: []
	Count int64 `gorm:"column:count;type:INT8;"`
	//[ 7] status                                         VARCHAR(32)          null: false  primary: false  isArray: false  auto: false  col: VARCHAR         len: 32      default: []
	Status string `gorm:"column:status;type:VARCHAR;size:32;"`
	//[ 8] close_reason                                   VARCHAR(255)         null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 255     default: []
	CloseReason sql.NullString `gorm:"column:close_reason;type:VARCHAR;size:255;"`
	//[ 9] create_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
	CreateTime time.Time `gorm:"column:create_time;type:TIMESTAMPTZ;"`
	//[10] update_time                                    TIMESTAMPTZ          null: false  primary: false  isArray: false  auto: false  col: TIMESTAMPTZ     len: -1      default: []
	UpdateTime time.Time `gorm:"column:update_time;type:TIMESTAMPTZ;"`
	//[11] summary                                        JSONB                null: true   primary: false  isArray: false  auto: false  col: JSONB           len: -1      default: []
	Summary datatypes.JSON `gorm:"column:summary;type:JSONB;"`
	//[12] wx_batch_id                                    VARCHAR(64)          null: true   primary: false  isArray: false  auto: false  col: VARCHAR         len: 64      default: []
	WxBatchID sql.NullString `gorm:"column:wx_batch_id;type:VARCHAR;size:64;"`
	//[13] mch_id                                         INT4                 null: false  primary: false  isArray: false  auto: false  col: INT4            len: -1      default: []
	MchID string `gorm:"column:mch_id;type:VARCHAR;size:32"`
}

// TableName sets the insert table name for this struct type
func (t *Transfer) TableName() string {
	return "transfer"
}

const TransferStatusAwait string = "AWAIT"
const TransferStatusAccept string = "ACCEPTED"
const TransferStatusProcessing string = "PROCESSING"
const TransferStatusFinished string = "FINISHED"
const TransferStatusClosed string = "CLOSED"

func GenBatchNo(n time.Time) string {
	return fmt.Sprintf("T%s%s", n.Format(util.LayoutYYMMdd), util.RandString(6))
}

func NewTransfer(d *TransferDetail, batchNo string) *Transfer {
	n := util.GetNow()
	return &Transfer{
		OutBatchNo: batchNo,
		AppID:      d.AppID,
		MchID:      d.MchID,
		Remark:     "批量转账",
		Name:       "批量转账",
		Status:     TransferStatusAwait,
		CreateTime: n,
		UpdateTime: n,
	}
}

func (t *Transfer) AddDetail(d *TransferDetail) {
	t.Amount += d.Amount
	t.Count++
}

func (t *Transfer) CanTransfer() bool {
	return t.Status == TransferStatusAwait
}

func (t *Transfer) AfterTransferSuccess(wxBatchID string) {
	t.WxBatchID = util.AdaptNullString(wxBatchID)
	t.Status = TransferStatusAccept
	t.UpdateTime = util.GetNow()
}

// 提交转账失败 - 提交本身操作失败（非查询失败）
func (t *Transfer) TransferSubmitFailed(failReason string) {
	t.Status = TransferStatusClosed
	t.CloseReason = util.AdaptNullString(failReason)
	t.UpdateTime = util.GetNow()
}

func (t *Transfer) IsFinalState() bool {
	switch t.Status {
	case TransferStatusFinished, TransferStatusClosed:
		return true
	default:
		return false
	}
}

func (t *Transfer) IsAwaitSync() bool {
	switch t.Status {
	case TransferStatusAccept, TransferStatusProcessing:
		// 已受理，处理中状态需要同步
		return true
	default:
		return false
	}
}

// 更新中间状态
func (t *Transfer) UpdateInterState(status string) {
	t.Status = status
	t.UpdateTime = util.GetNow()
}

type TransferSummary struct {
	SuccessNum    int64 `json:"success_num"`
	SuccessAmount int64 `json:"success_amount"`
	FailNum       int64 `json:"fail_num"`
	FailAmount    int64 `json:"fail_amount"`
}

func (info *TransferSummary) ToJson() (r datatypes.JSON) {
	r, _ = json.Marshal(info)
	return
}

func (info *TransferSummary) ToStruct(r datatypes.JSON) {
	b, _ := r.MarshalJSON()
	_ = json.Unmarshal(b, info)
}

// 更新到最终状态
func (t *Transfer) UpdateFinalState(status string, summary *TransferSummary, closeReason string) {
	t.Status = status
	t.CloseReason = util.AdaptNullString(closeReason)
	t.Summary = summary.ToJson()
	t.UpdateTime = util.GetNow()
}
